///
/// Sistemas Gráficos - 6671
/// 1er cuatrimestre 2009
///
//#include "GL/glut.h"
//#include <stdlib.h>
//#include "Bspline.h"
//#include "Punto.h"
//#include <iostream.h>
//#include <vector.h>
//
//#define ANCHO	800
//#define ALTO	600
//
//char caption[]="Sistema Gráficos - 66.71 - 2009c1";
//
//static Bspline bs;
//void reshape(int w, int h)
//{
//   glViewport (0, 0, (GLsizei) w, (GLsizei) h);
//   glMatrixMode(GL_PROJECTION);
//   glLoadIdentity();
//   gluOrtho2D(0.0, (GLdouble)w, (GLdouble)h, 0.0);
//}
//
//void init(void)
//{
//}
//
//
//void display(void)
//{
//	///
//	glClear(GL_COLOR_BUFFER_BIT);
//   	glMatrixMode(GL_MODELVIEW);
//        glLoadIdentity();
//	//
//
//	// Draw
//
//
//
//			  int n,t;
//			  n=7;          // number of control points = n+1
//			  t=4;           // degree of polynomial = t-1
//			  std::vector<Punto> Control;
//			  Control.push_back(Punto(10,100,0));
//			  Control.push_back(Punto(200,100,0));
//			  Control.push_back(Punto(345,300,0));
//			  Control.push_back(Punto(400,250,0));
//			  Control.push_back(Punto(500,550,0));
//			  Control.push_back(Punto(550,150,0));
//			  Control.push_back(Punto(570,50,0));
//			  Control.push_back(Punto(600,100,0));
//			  bs.setPuntos(Control);
//			  int resolution = 100;  // how many points our in our output array
//			  bs.bspline(n, t,resolution);
//
//
//	///
//  	glutSwapBuffers();
//	///
//}
//
//
//
//
///* x, y coordenadas mouse cuando se presiona key
//*/
//void keyboard(unsigned char key, int x, int y)
//{
//	switch (key)
//	{
//	case 0x1b:
//		exit (1);
//		break;
//	}
//}
//
//void onidle(void) {
//	glutPostRedisplay();
//}
//
//int main(int argc, char** argv)
//{
//   glutInit(&argc, argv);
//   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
//   glutInitWindowSize (ANCHO, ALTO);
//   glutInitWindowPosition (100, 100);
//   glutCreateWindow (caption);
//   init ();
//   glutKeyboardFunc(keyboard);
//   glutDisplayFunc(display);
//   glutReshapeFunc(reshape);
//   glutIdleFunc(onidle);
//
//
//
//   glutMainLoop();
//
//   return 0;
//}


#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <stdio.h>
#include "Punto.h"
#include "Quad.h"
#include "Textura.h"
#include <GL/glut.h>
#include <cmath>
#include <iostream>
#include <vector>
#include "Camara.h"
#include "TestPunto.h"
#include "CppTester.h"
#include "Curva.h"
#include "Bspline.h"
#include "ImagenAnimada.h"
Punto vp[4] = { Punto(0.0, 0.0), Punto(0.0, 1.0), Punto(1.0, 1.0), Punto(1.0,
		0.0) };

Curva* curvaEditor = NULL; // curva generada por clics en el editor;
Curva* curvaEnElAire = NULL; // curva generada al mapear la del editor en "todo" el espacio
std::vector<Curva*> caminos; // diferentes curvas que van desde un punto de la cuadricula a un punto de la curva en el aire
Quad* quad = NULL;
Textura t;
Camara camara;
ImagenAnimada* im = NULL;

// Variables asociadas a única fuente de luz de la escena
float light_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
float light_position[3] = { 10.0f, 10.0f, 8.0f };
float light_ambient[4] = { 0.05f, 0.05f, 0.05f, 1.0f };

// Variables de control
bool view_grid = true;
bool view_axis = true;
bool edit_panel = true;

// Handle para el control de las Display Lists
GLuint dl_handle;
#define DL_AXIS (dl_handle+0)
#define DL_GRID (dl_handle+1)
#define DL_AXIS2D_TOP (dl_handle+2)

// Tamaño de la ventana
GLfloat window_size[2];
#define W_WIDTH window_size[0]
#define W_HEIGHT window_size[1]

#define TOP_VIEW_POSX	((int)((float)W_WIDTH*0.60f))
#define TOP_VIEW_W		((int)((float)W_WIDTH*0.40f))
#define TOP_VIEW_POSY	((int)((float)W_HEIGHT*0.60f))
#define TOP_VIEW_H		((int)((float)W_HEIGHT*0.40f))

void OnIdle(void) {
	glutPostRedisplay();
}

void DrawAxis() {
	glDisable(GL_LIGHTING);
	glBegin(GL_LINES);
	// X
	glColor3f(1.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glColor3f(0.0, 0.0, 0.0);
	glVertex3f(15.0, 0.0, 0.0);
	// Y
	glColor3f(0.0, 1.0, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glColor3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 15.0, 0.0);
	// Z
	glColor3f(0.0, 0.0, 1.0);
	glVertex3f(0.0, 0.0, 0.0);
	glColor3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 15.0);
	glEnd();
	glEnable(GL_LIGHTING);
}

void DrawAxis2DTopView() {
	glDisable(GL_LIGHTING);
	glBegin(GL_LINE_LOOP);
	// X
	glColor3f(0.0, 0.5, 1.0);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(1.0, 0.0, 0.0);
	glVertex3f(1.0, 1.0, 0.0);
	glVertex3f(0.0, 1.0, 0.0);
	glEnd();
	glBegin(GL_QUADS);
	glColor3f(0.1, 0.1, 0.1);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(1.0, 0.0, 0.0);
	glVertex3f(1.0, 1.0, 0.0);
	glVertex3f(0.0, 1.0, 0.0);
	glEnd();

	glEnable(GL_LIGHTING);
}

void DrawXYGrid() {
	int i;
	glDisable(GL_LIGHTING);
	glColor3f(0.15, 0.1, 0.1);
	glBegin(GL_LINES);
	for (i = -20; i < 21; i++) {
		glVertex3f(i, -20.0, 0.0);
		glVertex3f(i, 20.0, 0.0);
		glVertex3f(-20.0, i, 0.0);
		glVertex3f(20.0, i, 0.0);
	}
	glEnd();
	glEnable(GL_LIGHTING);
}
void Set3DEnv() {
	glViewport(0, 0, (GLsizei) W_WIDTH, (GLsizei) W_HEIGHT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat) W_WIDTH / (GLfloat) W_HEIGHT, 0.10, 100.0);
}

void SetPanelTopEnv() {
	glViewport(TOP_VIEW_POSX, TOP_VIEW_POSY, (GLsizei) TOP_VIEW_W,
			(GLsizei) TOP_VIEW_H);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(-0.10, 1.05, -0.10, 1.05);
}

void init(void) {
	dl_handle = glGenLists(3);

	glClearColor(0.02, 0.02, 0.04, 0.0);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);

	// Generación de las Display Lists
	glNewList(DL_AXIS, GL_COMPILE);
	DrawAxis();
	glEndList();
	glNewList(DL_GRID, GL_COMPILE);
	DrawXYGrid();
	glEndList();
	glNewList(DL_AXIS2D_TOP, GL_COMPILE);
	DrawAxis2DTopView();
	glEndList();

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	quad = new Quad(vp);
	t.load("./image.bmp");
	curvaEnElAire =  new Bspline();
	curvaEnElAire->dibujarCurvaRandom(Punto(0,0,0),Punto(10,10,10));
	im =  new ImagenAnimada(*quad,&t);
	im->setCamino(curvaEnElAire);
}

void display(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	///////////////////////////////////////////////////
	// Escena 3D
	Set3DEnv();

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	camara.ponerCamara();

	if (view_axis)
		glCallList(DL_AXIS);

	if (view_grid)
		glCallList(DL_GRID);
	//
	///////////////////////////////////////////////////
    im->display();
    curvaEnElAire->pintarCurva();
	///////////////////////////////////////////////////
	//
	// Panel 2D para la vista superior
	if (edit_panel) {
		SetPanelTopEnv();
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt(0, 0, 0.5, 0, 0, 0, 0, 1, 0);
		glCallList(DL_AXIS2D_TOP);
	}
	//
	///////////////////////////////////////////////////

	glutSwapBuffers();

}

void reshape(int w, int h) {
	W_WIDTH = w;
	W_HEIGHT = h;
}

void agreparPunto(int x, int y) {
	//TODO
}

void mouse(int button, int state, int x, int y) {
	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
		if ((edit_panel && (x > TOP_VIEW_POSX) && (x < (TOP_VIEW_POSX
				+ TOP_VIEW_W)))
				&& ((y < (W_HEIGHT - TOP_VIEW_POSY)) && (y > 0))) {
			std::cout << "Click en: [ " << x << " ; " << y << " ]" << std::endl;
			agreparPunto(x, y);
		}
	}
}

void keyboard(unsigned char key, int x, int y) {

	switch (key) {
	case 'q':
		exit(0);
		break;
	case 'g':
		view_grid = !view_grid;
		glutPostRedisplay();
		break;
	case 'a':
		view_axis = !view_axis;
		glutPostRedisplay();
		break;
	case 'e':
		edit_panel = !edit_panel;
		glutPostRedisplay();
		break;
	case 'r':
		im->reproducir();
		glutPostRedisplay();
		break;
	case 'b':
		im->rebobinar();
		glutPostRedisplay();
		break;
	case 'p':
		im->pausar();
		glutPostRedisplay();
		break;
	default:
		break;
	}
}

void ArrowKey(int key, int x, int y) {
	switch (key) {
	case GLUT_KEY_RIGHT:
		camara.moverDerecha();
		glutPostRedisplay();
		break;
	case GLUT_KEY_LEFT:
		camara.moverIzquierda();
		glutPostRedisplay();
		break;
	case GLUT_KEY_UP:
		camara.moverArriba();
		glutPostRedisplay();
		break;
	case GLUT_KEY_DOWN:
		camara.moverAbajo();
		glutPostRedisplay();
		break;
	}

}

int main(int argc, char** argv) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(1024, 768);
	glutInitWindowPosition(0, 0);

	glutCreateWindow(argv[0]);
	glutFullScreen();
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutSpecialFunc(ArrowKey);
	glutMouseFunc(mouse);
	glutIdleFunc(OnIdle);


	CppTester tester;
	tester.agregarTest(new TestPunto(),"Test Punto");
	tester.correrTests();

	glutMainLoop();

	delete quad;
	delete im;
	delete curvaEnElAire;
	return 0;
}
